package websocket;

import com.alibaba.fastjson.JSONObject;
import entity.GameRoom;
import entity.Player;
import entity.User;
import game.Z5CGame;
import service.*;
import top.RuntimeContextProvider;
import utils.Common;

import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;

import static game.Z5CGame.Camp.black;
import static game.Z5CGame.Camp.white;
import static game.Z5CGame.GameResult.blackWin;
import static service.UserService.UserGameResult.lost;
import static service.UserService.UserGameResult.win;
import static service.UserService.UserGameResult.run;
import static service.UserService.UserGameResult.draw;

@ServerEndpoint(value = "/battleRoom")
public class Battlefield {

    private static WebsocketSessionService websocketSessionService = RuntimeContextProvider.getContextProvider().getWebsocketSessionService();
    private static UserService userService = RuntimeContextProvider.getContextProvider().getUserService();
    private static PlayerService playerService = RuntimeContextProvider.getContextProvider().getPlayerService();
    private static GameRoomService gameRoomService = RuntimeContextProvider.getContextProvider().getGameRoomService();

    /* 连接时
     * 首先连上websocket，在onOpen中注册session，并将session的id发送给前端页面
     * 前端页面向PlayerServlet发请求，发送session的id，PlayerServlet从WebsocketSessionService中获取session
     * PlayerServlet向PlayerService注册Session和User
     * PlayerService创建Player，注入Session和User信息，并注册该Player
     * PlayerServlet创建房间
     */

    /*
     * 房间默认不可见
     * 当设置好房间后才可见
     */

    /**
     *
     * @param session
     */
    @OnOpen
    public void onOpen(Session session) {
        //注册session
        websocketSessionService.putSession(session);
        //向前端页面发送 Websocket Session ID
        JSONObject json = new JSONObject();
        json.put("type", "getWSSessionId");
        json.put("WSID", session.getId());
        session.getAsyncRemote().sendText(json.toJSONString());
    }

    private static void configRoom(Session session, JSONObject message) {
        Player player = playerService.getBySession(session);
        GameRoom room = null;
        if (
            player == null ||                        //玩家未注册
            (room = player.getGameRoom()) == null || //玩家未进入房间
            player != room.getHost() ||              //玩家不是房主，不能更改房间设置
            room.getReady() == 2                     //游戏进行时不能改变房间设置
        ) return;

        room.configRoom(message);
    }

    private static GameRoom playerReady(Session session, JSONObject message) {
        Player me = playerService.getBySession(session);
        GameRoom room = null;
        if (
            me == null ||                                        //玩家未注册
            (room = me.getGameRoom()) == null ||                 //玩家未进入房间
            (me != room.getHost() && me != room.getChallenger()) //不是玩家（是观众）
        ) return null;

        Player peer = (room.getHost() == me? room.getChallenger(): room.getHost());

        String campName = message.getString("cmp");
        Z5CGame.Camp myCamp = Z5CGame.Camp.valueOf(campName);
        //String negaCampName = (myCamp == Z5CGame.Camp.black? "white": "black");
        String myHeadPic = me.getUserInfo().getHeadPic();
        if(Common.isStringEmpty(myHeadPic)) myHeadPic = "default";

        switch(campName) {
            case "black": case "white": {
                if (
                    peer == null ||
                    peer.getCamp() == null ||
                    peer.getCamp() != myCamp

                ) {
                    JSONObject json = new JSONObject();
                    json.put("type", "getReady");
                    json.put("cmp", campName);

                    switch(me.getCamp()) { //原先的
                        case none:
                            me.setCamp(myCamp);
                            me.setReady(true);
                            json.put(campName + "Img", myHeadPic);
                            break;
                        case black: //原来是黑子
                            json.put("blackImg", "none");
                            if(myCamp == white) { //现选白子
                                me.setCamp(white);
                                json.put("whiteImg", myHeadPic);
                            } else { //取消黑子
                                me.setCamp(Z5CGame.Camp.none);
                                me.setReady(false);
                            }
                            break;
                        case white: //原来是白子
                            json.put("whiteImg", "none");
                            if(myCamp == black) { //现选黑子
                                me.setCamp(black);
                                json.put("blackImg", myHeadPic);
                            } else { //取消白子
                                me.setCamp(Z5CGame.Camp.none);
                                me.setReady(false);
                            }
                            break;
                    }
                    if(room.checkPlayerReady() < 2) {
                        String result = json.toJSONString();
                        session.getAsyncRemote().sendText(result);
                        if(peer != null) peer.getSession().getAsyncRemote().sendText(result);
                        room.broadcastToWatcher(result);
                    }
                }
                break;
            }
        }
        return room;
    }

    @OnMessage
    public void messageDispatcher(Session session, String msg) {

        JSONObject message = JSONObject.parseObject(msg);

        switch((String)message.get("type")) {
            case "configRoom":
                configRoom(session, message);
                break;
            case "requestGo":
            case "requestRollback": {
                Player p = playerService.getBySession(session);
                Z5CGame.GameResult result = p.play(msg).getResult();

                switch(result) {
                    case blackWin: case whiteWin: {

                        GameRoom room = p.getGameRoom();

                        Player host = room.getHost();
                        Player challenger = room.getChallenger();

                        long scoreWinLost[] = null;
                        User winner;
                        User loser;

                        if(host.getCamp() == (result == blackWin? black: white)) {
                            winner = host.getUserInfo();
                            loser  = challenger.getUserInfo();
                        } else {
                            winner = challenger.getUserInfo();
                            loser  = host.getUserInfo();
                        }

                        scoreWinLost =
                                top.RuntimeContextProvider.getContextProvider().getRule().calculatePointsWhenWinLost(winner, loser);

                        userService.commitGameResult(winner, win, scoreWinLost[0]);
                        userService.commitGameResult(loser, lost, scoreWinLost[1]);
                        top.RuntimeContextProvider.getContextProvider().getRankingService().update(winner);
                        top.RuntimeContextProvider.getContextProvider().getRankingService().update(loser);

                        host.setReady(false);
                        challenger.setReady(false);
                        room.reset();
                        break;
                    } case draw: {
                        GameRoom room = p.getGameRoom();

                        long scoreDraw[] = null;
                        User a = room.getHost().getUserInfo();
                        User b = room.getChallenger().getUserInfo();

                        scoreDraw =
                                top.RuntimeContextProvider.getContextProvider().getRule().calculatePointsWhenDraw(a, b);

                        userService.commitGameResult(a, draw, scoreDraw[0]);
                        userService.commitGameResult(b, draw, scoreDraw[1]);
                        top.RuntimeContextProvider.getContextProvider().getRankingService().update(a);
                        top.RuntimeContextProvider.getContextProvider().getRankingService().update(b);

                        room.getHost().setReady(false);
                        room.getChallenger().setReady(false);
                        room.reset();
                        break;
                    }
                }
                break;
            } case "playerReady": {
                GameRoom room;
                if((room = playerReady(session, message)) != null && room.getReady() == 2) {
                    room.cleanConfirm();
                    room.configPlayerCheckerboard();
                }
                break;
            } case "playerConfirm": {
                GameRoom room = playerService.getBySession(session).getGameRoom();
                int cfm = room.playerConfirm();
                System.out.println("confirmed: " + cfm);
                if(cfm == 2) {
                    room.broadcast(Message.PLAYERCONFIRM);
                }
                break;
            }
        }
    }

    @OnClose
    public void onClose(Session session) throws IOException {
        Player p = playerService.getBySession(session);
        //防止因用户未登录直接进入battle页面而引发NullPointerException
        if(p == null) return;

        GameRoom gr = p.getGameRoom();
        User user = p.getUserInfo();

        if(gr != null) {
            if(gr.getReady() == 2) {

                User peer = (user == gr.getHost().getUserInfo()? gr.getChallenger().getUserInfo(): gr.getHost().getUserInfo());
                long scoreWinLost[] = null;
                scoreWinLost =
                        top.RuntimeContextProvider.getContextProvider().getRule().calculatePointsWhenRun(peer, user);

                //提交玩家的逃跑记录
                userService.commitGameResult(user, run, scoreWinLost[1]);
                userService.commitGameResult(peer, win, scoreWinLost[0]);
                top.RuntimeContextProvider.getContextProvider().getRankingService().update(user);
                top.RuntimeContextProvider.getContextProvider().getRankingService().update(peer);
            }
        }

        //玩家离开房间，重启游戏
        gr = p.leaveRoom();

        if(gr != null && gr.getPlayerCount() == 0) {
            //如果房间存在但为空，删除房间
            gameRoomService.removeRoom(gr);
        }

        //注销玩家
        playerService.unregist(session);
    }
}
